'use strict';
    
function loadNode(nodeId, callback)
{
    var raw;
    $("#"+htConfig.statusDivId).html
    (
        '<img src="loading-small.gif" /> Loading Node '+nodeId
        );
    
    $.get(htConfig.backendUrl+"?id="+nodeId+"&dep=3", function(str)
          {
            $("#"+htConfig.statusDivId).html( '<img src="loading-small.gif /> Parsing data of node '+nodeId);
            raw = eval(str);
            $("#"+htConfig.loadingDivId).hide();
            initilize(raw);
            $("#"+htConfig.statusDivId).html( '<img src="loading-small.gif /> Rendering node '+nodeId);
            if (callback)
                callback();
            $("#"+htConfig.statusDivId).html( 'Succeeded to load node '+nodeId);
        });
}
    

var a = {};

function normalize(x,y)
{
    var len=Math.sqrt(x*x+y*y) / 20;
    return [x/len, y/len];
}

function rotate(x,y,theta)
{
    var co=Math.cos(theta), si=Math.sin(theta);
    return [x*co - y*si, x*si +y*co];
}

function NodeInfo(id, name, info, edgeFrom, edgeTo) {
    this.id = id;
    this.name = name;
    this.info = info;
    this.edgeFrom = [];
    for (var i=0; i<edgeFrom.length; ++i)
        if (a[edgeFrom[i]])
        {
            var ok=false;
            if (a[edgeFrom[i]].edgeTo)
            {
            for(var j=0; j<a[edgeFrom[i]].edgeTo.length; ++j)
                if (a[edgeFrom[i]].edgeTo[j]==id)
                {
                    ok=true;
                    break;
                }
            if (!ok)
                a[edgeFrom[i]].edgeTo.push(id);
            }
            this.edgeFrom.push(edgeFrom[i]);
        }
    this.edgeTo = [];
    for (var i=0; i<edgeTo.length; ++i)
        if (a[edgeTo[i]])
        {
            var ok=false;
            if (a[edgeTo[i]].edgeFrom)
            {
            for(var j=0; j<a[edgeTo[i]].edgeFrom.length; ++j)
                if (a[edgeTo[i]].edgeFrom[j]==id)
                {
                    ok=true;
                    break;
                }
            if (!ok)
                a[edgeTo[i]].edgeFrom.push(id);
            }
            this.edgeTo.push(edgeTo[i]);
        }
}

function makeSVG(tag, attrs) {
    "use strict";
    var k, el = document.createElementNS('http://www.w3.org/2000/svg', tag);
    for (k in attrs)
    {
        el.setAttribute(k, attrs[k]);
    }
    return el;
}

function setNodePos(id, x, y) {
    $("#"+htConfig.labelPrefix + id).css("left", x + "px");
    $("#"+htConfig.labelPrefix + id).css("top", y + "px");
    $("#"+htConfig.nodePrefix + id).attr("cx", x);
    $("#"+htConfig.nodePrefix + id).attr("cy", y);
}

function getNodePosX(id) {
    return parseInt($("#"+htConfig.nodePrefix + id).attr('cx'));
}

function getNodePosY(id) {
    return parseInt($("#"+htConfig.nodePrefix + id).attr('cy'));
}

function drawArrowLine(id1, id2) {
    var sg = document.getElementById(htConfig.svgId);
    sg.appendChild(
        makeSVG('line', {
            id: htConfig.linePrefix + id1 + "to" + id2,
            class: "line"
        })
    );
    updateArrowLine(id1, id2);
}

function updateArrowLine(id1, id2)
{
    $("#"+htConfig.linePrefix+id1+"to"+id2).attr("x1", getNodePosX(id1))
    .attr("y1", getNodePosY(id1))
    .attr("x2", getNodePosX(id2))
    .attr("y2", getNodePosY(id2));
}

function drawArrow(id1, id2) {
    var sg = document.getElementById(htConfig.svgId);
    sg.appendChild(
        makeSVG('polygon', {
            id: htConfig.arrowPrefix + id1 + "to" + id2,
            class: "polygon"
        })
    );
    updateArrow(id1, id2);
}

function updateArrow(id1, id2)
{
    var arr = $('#'+htConfig.arrowPrefix+id1+"to"+id2);
    var vec = normalize(getNodePosX(id1)-getNodePosX(id2),
                        getNodePosY(id1)-getNodePosY(id2));
    var ed= [vec[0]/1.5, vec[1]/1.5];
    var vec1 = rotate(vec[0], vec[1], 0.3);
    var vec2 = rotate(vec[0], vec[1], -0.3);
    var x2=getNodePosX(id2);
    var y2=getNodePosY(id2);
    arr.attr("points", x2+","+y2+"   "+
            (x2+vec1[0])+","+(y2+vec1[1])+"   "+
            (x2+ed[0]) +","+(y2+ed[1])+"   "+
            (x2+vec2[0])+","+(y2+vec2[1]) );
}
    
function drawNode(nodeId)
{
    var sg = document.getElementById(htConfig.svgId);
    var lab = $("#"+htConfig.labelDivId);
    if (nodePrint(nodeId))
    {
        $('#'+htConfig.labelPrefix+nodeId).show();
        $('#'+htConfig.nodePrefix+nodeId).show();
        return ;
    }
    sg.appendChild(
            makeSVG('circle', {
                id: htConfig.nodePrefix + nodeId,
                class: "node",
                r: 6
            }));
    var idname = htConfig.labelPrefix + nodeId;
    lab.append('<div id="'+ htConfig.labelPrefix + nodeId +
            '"class="label"  >' +
            a[nodeId].name +
            '</div> ');
        var nd = $("#" + idname);
        nd.data("num", nodeId)
        .mousedown(StartMove)
        .mouseup(StopMove)
        .mouseover(MouseOverGen(nodeId))
        .mouseout(MouseOutGen(nodeId));
    $("#" +htConfig.nodePrefix + nodeId).addClass("node");
    setNodePos(nodeId, Math.random() * 500, Math.random() * 400);
}

function nodeExist(nodeId)
{
    return Boolean(a[nodeId]);
}

function edgePrint(nodeId1, nodeId2)
{
    return Boolean(document.getElementById(htConfig.linePrefix+nodeId1+'to'+nodeId2));
}

function nodePrint(nodeId)
{
    return Boolean(document.getElementById(htConfig.labelPrefix+nodeId));
}

function drawEdge(nodeId1, nodeId2)
{
    if (edgePrint(nodeId1, nodeId2))
    {
        $('#'+htConfig.linePrefix+nodeId1+'to'+nodeId2).show();
        $('#'+htConfig.arrowPrefix+nodeId1+'to'+nodeId2).show();
        return;
    }
    drawArrowLine(nodeId1, nodeId2);
    drawArrow(nodeId1, nodeId2);
}

function hideOutsideEdge(nodeId)
{
    for(var i=0; i<a[nodeId].edgeTo.length; ++i)
    {
        $("#"+htConfig.arrowPrefix+nodeId+"to"+a[nodeId].edgeTo[i]).hide();
        $("#"+htConfig.linePrefix+nodeId+"to"+a[nodeId].edgeTo[i]).hide();
    }
}
    

function drawOutsideEdge(nodeId)
{
    for(var i=0; i<a[nodeId].edgeTo.length; ++i)
        drawEdge(nodeId, a[nodeId].edgeTo[i]);
}
    
    
function initilize(d) {
    var lab = $("#"+htConfig.labelDivId);
    var sg = document.getElementById(htConfig.svgId);
    for (var i = 0; i < d.length; ++i)
    {
        a[d[i].id] = true;
    }
    for (var i = 0; i < d.length; ++i) 
    {
        var x = d[i];
        a[x.id] = new NodeInfo(x.id, x.name, x.info, x.edgeFrom, x.edgeTo);
    }

    $("#"+htConfig.drawOnDivId).mousedown(globalDragStart);
    return false;
}

function clickNode(nodeId)
{
    loadNode(nodeId, function()
             {
                $("#"+htConfig.svgId).empty();
                $("#"+htConfig.labelDivId).empty();
                drawNode(nodeId);
                setNodePos(nodeId, $("#"+htConfig.drawOnDivId).width() / 2, $("#"+htConfig.drawOnDivId).height() /2);
                rearrangeNode(nodeId);
    });
}
    

function nodeClick(e)
{
    var obj = e.target;
    var n = $("#" + obj.id).data("num");
    clickNode(n);
}

function hideAll()
{
    $('polygon[id^="'+htConfig.arrowPrefix+'"]').hide();
    $('line[id^="'+htConfig.linePrefix+'"]').hide();
    $('circle[id^="'+htConfig.nodePrefix+'"]').hide();
    $(".label").hide();
    $('.label').css('opacity','0.8')
    .css('z-index', '100');
    $('circle[id^="'+htConfig.nodePrefix+'"]').css('fill-opacity','0.6')
    .css('stroke-opacity','0.6')
    .attr('r', '6');
    $('line[id^="'+htConfig.linePrefix+'"]').css('stroke-opacity', '1.0');
    $('polygon[id^="'+htConfig.arrowPrefix+'"]').css('fill-opacity', '1.0');
}

function globalDragStart(e)
{
    offset_x = parseInt(e.clientX);
    offset_y = parseInt(e.clientY);
    for(var w in a)
    {
        a[w].lastX = getNodePosX(w);
        a[w].lastY = getNodePosY(w);
    }
    document.getElementById(htConfig.drawOnDivId).onmousemove = function(ev) {
        'use strict';
        var cur_x = parseInt(ev.clientX);
        var cur_y = parseInt(ev.clientY);
        var off_x = cur_x - offset_x;
        var off_y = cur_y - offset_y;
        for (var ww in a)
        {
            setNodePos(ww, a[ww].lastX+off_x, a[ww].lastY+off_y);
            updateRelatedEdge(ww);
        }
    };
    document.getElementById(htConfig.drawOnDivId).onmouseup = globalDragEnd ;
            
}

function globalDragEnd()
{
    document.getElementById(htConfig.drawOnDivId).onmousemove = null;
    document.getElementById(htConfig.drawOnDivId).onmouseup = null;
    console.log('end');
}

var lastHTML='';

function MouseOverGen(nodeId)
{
    return function() {
        console.log("gen"+nodeId);
        lastHTML = $("#"+htConfig.infoDivId).html();
        $("#"+htConfig.infoDivId).html( a[nodeId].info);
        $("#"+htConfig.nodePrefix+nodeId).css("fill-opacity", "1.0")
        .css("z-index", "105");
        $("#"+htConfig.nodePrefix+nodeId).css("z-index", "105");
        for(var i=0; i<a[nodeId].edgeFrom.length; ++i)
        {
            $('#'+htConfig.linePrefix+a[nodeId].edgeFrom[i]+"to"+nodeId).css("stroke-opacity", "1.0").css(
                "stroke-width","3px")
            .css("z-index", "104");
            $('#'+htConfig.arrowPrefix+a[nodeId].edgeFrom[i]+"to"+nodeId).css("fill-opacity", "1.0");
            $("#"+htConfig.nodePrefix+a[nodeId].edgeFrom[i]).css("fill","green")
            .attr("r","9")
            .css("z-index", "104");
        }
        for(var i=0; i<a[nodeId].edgeTo.length; ++i)
        {
            $('#'+htConfig.linePrefix+nodeId+"to"+a[nodeId].edgeTo[i]).css("stroke-opacity", "1.0").css(
                "stroke-width","3px");
            $('#'+htConfig.arrowPrefix+nodeId+"to"+a[nodeId].edgeTo[i]).css("fill-opacity", "1.0");
            $("#"+htConfig.nodePrefix+a[nodeId].edgeTo[i]).css("fill","yellow");
            $("#"+htConfig.nodePrefix+a[nodeId].edgeTo[i]).attr("r","3");
        }
    }
}

function MouseOutGen(nodeId)
{
    return function() {
        $("#"+htConfig.infoDivId).html(lastHTML);
        console.log("gen"+nodeId);
        $("#"+htConfig.nodePrefix+nodeId).css("fill-opacity", "0.6");
        for(var i=0; i<a[nodeId].edgeFrom.length; ++i)
        {
            $('#'+htConfig.linePrefix+a[nodeId].edgeFrom[i]+"to"+nodeId).css("stroke-opacity", "0.6").css(
                "stroke-width","1px");
            $('#'+htConfig.arrowPrefix+a[nodeId].edgeFrom[i]+"to"+nodeId).css("fill-opacity", "0.6");
            $("#"+htConfig.nodePrefix+a[nodeId].edgeFrom[i]).css("fill","rgb(220,122,107)");
            $("#"+htConfig.nodePrefix+a[nodeId].edgeFrom[i]).attr("r","6");
            
        }
        for(var i=0; i<a[nodeId].edgeTo.length; ++i)
        {
            $('#'+htConfig.linePrefix+nodeId+"to"+a[nodeId].edgeTo[i]).css("stroke-opacity", "0.6").css(
                "stroke-width","1px");
            $('#'+htConfig.arrowPrefix+nodeId+"to"+a[nodeId].edgeTo[i]).css("fill-opacity", "0.6");
            $("#"+htConfig.nodePrefix+a[nodeId].edgeTo[i]).css("fill","rgb(220,122,107)");
            $("#"+htConfig.nodePrefix+a[nodeId].edgeTo[i]).attr("r","6");
        }
    }
}

var offset_x;
var offset_y;
var objnum;
var last_x;
var last_y;

function StartMove(oEvent) {
    oEvent.stopPropagation();
    var whichButton;
    globalDragEnd();
    if (document.all && oEvent.button == 1) whichButton = true;
    else {
        if (oEvent.button == 0) whichButton = true;
    }
    if (whichButton) {
        var obj = oEvent.target;
        objnum = $("#" + obj.id).data("num");
        console.log("start" + objnum);
        var oDiv = obj;
        offset_x = parseInt(oEvent.clientX - oDiv.offsetLeft);
        offset_y = parseInt(oEvent.clientY - oDiv.offsetTop);
        document.onmouseup = StopMove;
        last_x = oEvent.clientX;
        last_y = oEvent.clientY;
        document.onmousemove = function (mEvent) {
            "use strict";
            var eEvent;

            if (document.all) eEvent = event;
            else {
                eEvent = mEvent;
            }
            var oDiv = obj;
            updateRelatedEdge(objnum);
            var x = eEvent.clientX - offset_x;
            var y = eEvent.clientY - offset_y;

            if (oDiv.parentNode.offsetWidth > x + oDiv.offsetWidth && x > 0 && y > 0) {
                oDiv.style.left = (x) + "px";
                oDiv.style.top = (y) + "px";
            }
            $("#"+htConfig.nodePrefix + objnum).attr("cx", $("#span" + objnum).position().left);
            $("#"+htConfig.nodePrefix + objnum).attr("cy", $("#span" + objnum).position().top);
        }
    }
}

function StopMove(oEvent)

{
    if (last_x == oEvent.clientX && last_y == oEvent.clientY)
    {
        var obj = document.getElementById('span'+objnum);
        obj.onclick = nodeClick;
        obj.click();
        obj.onclick = null;
    }
    updateRelatedEdge(objnum);
    document.onmousemove = null;
    document.onmouseup = null;

}

function getLine(node1, node2)
{
    "use strict";
    return $(htConfig.linePrefix+node1+"to"+node2);
}

function getArrow(node1, node2)
{
    "use strict";
    return $(htConfig.arrowPrefix+node1+"to"+node2);
}

function updateRelatedEdge(nodeId)
{
    "use strict";
    var e,xx,i;
    xx=a[nodeId];
    for(i=0; i<xx.edgeTo.length; ++i)
    {
        updateArrowLine(nodeId, xx.edgeTo[i]);
        updateArrow(nodeId, xx.edgeTo[i]);
    }
    for(i=0; i<xx.edgeFrom.length; ++i)
    {
        updateArrowLine(xx.edgeFrom[i], nodeId);
        updateArrow(xx.edgeFrom[i], nodeId);
    }
}

function drawRelatedNode(nodeId)
{
    drawNode(nodeId);
    for(var i=0; i<a[nodeId].edgeFrom.length; ++i)
    {
        var x = a[nodeId].edgeFrom[i];
        if (Math.abs(a[x].d)>2)
        {
            drawNode(x);
            setNodePos(x, getNodePosX(nodeId)+ (100*(Math.random()-0.5)), getNodePosY(nodeId)-40);
            drawEdge(x, nodeId);
            updateRelatedEdge(x);
            setAsUnimportantNode(x);
        }
        else
            drawEdge(x, nodeId);
    }
    for(var i=0; i<a[nodeId].edgeTo.length; ++i)
    {
        var x = a[nodeId].edgeTo[i];
        if (Math.abs(a[x].d)>2)
        {
            drawNode(x);
            setNodePos(x, getNodePosX(nodeId)+ (100*(Math.random()-0.5)), getNodePosY(nodeId)+40);
            drawEdge(nodeId, x);
            updateRelatedEdge(x);
            setAsUnimportantNode(x);
        }
        else
            drawEdge(nodeId, x);
    }
    updateRelatedEdge(nodeId);
}

function eachRelatedEdge(nodeId, f)
{
    for(var i=0; i<a[nodeId].edgeFrom.length; ++i)
    {
        f(getLine(a[nodeId].edgeFrom[i], nodeId));
        f(getArrow(a[nodeId].edgeFrom[i], nodeId));
    }
    for(var i=0; i<a[nodeId].edgeTo.length; ++i)
    {
        f(getLine(nodeId, a[nodeId].edgeTo[i]));
        f(getArrow(nodeId, a[nodeId].edgeTo[i]));
    }
}
        

function setAsUnimportantNode(nodeId)
{
    $('#'+htConfig.labelPrefix+nodeId).css('opacity','0.2')
    .css('z-index', '1');
    $('#'+htConfig.nodePrefix+nodeId).css('fill-opacity','0.2')
    .css('stroke-opacity','0.2');
    $('#'+htConfig.nodePrefix+nodeId).attr('r', '3');
    eachRelatedEdge(nodeId, function(obj){
        obj.css('stroke-opacity','0.2');
        obj.css('fill-opacity', '0.2');
    });
}

function updateNodePos(nodeId)
{
    setNodePos(nodeId, $("#"+htConfig.nodePrefix+nodeId).position().left, $("#"+htConfig.nodePrefix+nodeId).position().top);
}

function rearrangeNode(nodeId)
{
    hideAll();
    for (var x in a)
    {
        a[x].d = 100000;
    }
    getUp(nodeId, 0);
    getDown(nodeId, 0);
    var dt = [];
    dt[-2] = [];
    dt[-1] = [];
    dt [0] = [];
    dt[1] = [];
    dt[2] = [];
    a[nodeId].d = 0;
    drawNode(nodeId);
    setNodePos(nodeId, $("#"+htConfig.drawOnDivId).width() / 2, $("#"+htConfig.drawOnDivId).height() /2);
    for (var x in a)
    {
        if (a[x] && Math.abs(a[x].d) <= 2)
        {
            dt[a[x].d].push(x);
            drawNode(x);
        }
    }
    var px = getNodePosX(nodeId), py = getNodePosY(nodeId);
    for (var i=0; i<dt[-1].length; ++i)
    {
        setNodePos(dt[-1][i], px-250 + (i+1) * 500/(dt[-1].length+1) + 10*(Math.random()-0.5), py-150);
        drawRelatedNode(dt[-1][i]);
    }
    for (var i=0; i<dt[-2].length; ++i)
    {
        setNodePos(dt[-2][i], px-300 + (i+1) * 600 /(dt[-2].length+1)+ 10*(Math.random()-0.5), py-300);
        drawRelatedNode(dt[-2][i]);
    }
    for (var i=0; i<dt[1].length; ++i)
    {
        setNodePos(dt[1][i], px-250 + (i+1) * 500/(dt[1].length+1)+ 10*(Math.random()-0.5), py+150);
        drawRelatedNode(dt[1][i]);
    }
    for (var i=0; i<dt[2].length; ++i)
    {
        setNodePos(dt[2][i], px-300 + (i+1) * 600 /(dt[2].length+1)+ 10*(Math.random()-0.5), py+300);
        drawRelatedNode(dt[2][i]);
    }
    updateRelatedEdge(nodeId);
    
    
}
function getUp(nodeId, dep)
{
    if (!a[nodeId].d || dep < Math.abs(a[nodeId].d))
        a[nodeId].d = -dep;
    if (dep==2)
    {
        return false;
    }
    for(var i=0; i<a[nodeId].edgeFrom.length; ++i)
    {
        getUp(a[nodeId].edgeFrom[i], dep+1);
    }
}

function getDown(nodeId, dep)
{
    if (!a[nodeId].d || dep < Math.abs(a[nodeId].d))
        a[nodeId].d = dep;
    if (dep==2)
    {
        return false;
    }
    for(var i=0; i<a[nodeId].edgeTo.length; ++i)
    {
        getDown(a[nodeId].edgeTo[i], dep+1);
    }
}
    
    

function click() {
    console.log("click");
}